package com.yuan.yblog.portal.service;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.yuan.yblog.common.api.ResultCode;
import com.yuan.yblog.common.consts.Constants;
import com.yuan.yblog.common.consts.RedisKey;
import com.yuan.yblog.common.exception.Asserts;
import com.yuan.yblog.common.service.EntityCacheService;
import com.yuan.yblog.common.utils.IpUtils;
import com.yuan.yblog.common.utils.StringUtils;
import com.yuan.yblog.crud.entity.User;
import com.yuan.yblog.crud.service.IUserService;
import com.yuan.yblog.portal.asyn.AsyncFactory;
import com.yuan.yblog.portal.asyn.AsyncManager;
import com.yuan.yblog.portal.pojo.VO.UserLoginSuccessVO;
import com.yuan.yblog.security.pojo.LoginUser;
import com.yuan.yblog.security.util.JwtTokenUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * @author hepeiyuan on 2024/5/20 16:25
 * @description 认证授权相关服务类
 */
@Service
@Slf4j
public class AuthenticationService {

    @Autowired
    EntityCacheService entityCacheService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    HttpServletRequest request;

    @Autowired
    IUserService iUserService;

    @Autowired
    AuthorityService authorityService;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    /**
     * 用户登录
     * @param userEmail 邮箱
     * @param password 明文密码
     */
    public UserLoginSuccessVO login(String userEmail, String password) {
        String token = null;
        Authentication loginAuthentication = null;
        try{
            //封装用户信息
            UsernamePasswordAuthenticationToken userAuthentication = new UsernamePasswordAuthenticationToken(userEmail, password);
            //密码校验，该方法会去调用UserDetailsService.loadUserByUsername
            loginAuthentication = authenticationManager.authenticate(userAuthentication);
        } catch (AuthenticationException e) {
            log.error("登录异常:{}", e.getMessage());
        }
        if(loginAuthentication == null){
            AsyncManager.me().execute(AsyncFactory.recordLoginInfo(userEmail, Constants.LOGIN_FAIL, "密码校验不通过"));
            throw new UsernameNotFoundException("邮箱或密码错误");
        }

        // 生成token
        token = jwtTokenUtil.generateTokenByUserInfo((UserDetails) loginAuthentication.getPrincipal());

        // 获取用户
        LoginUser loginUser = (LoginUser) loginAuthentication.getPrincipal();
        User user = loginUser.getUser();

        // 记录登录信息
        AsyncManager.me().execute(AsyncFactory.recordLoginInfo(userEmail, Constants.LOGIN_SUCCESS, "user.login.success"));

        // 更新数据库（用户最后登录时间）
        boolean success = iUserService.updateAndDeleteCache(user.getId(), new UpdateWrapper<User>()
                .eq("id",user.getId())
                .set("lasted",new Date())
        );

        // 操作上线
        loginUser.setIp(IpUtils.getIpAddr(request));
        loginUser.setToken(token);
        if (!success || !buildLoginUser(loginUser)) {
            Asserts.fail("登录失败");
        }

        // 返回结果
        return new UserLoginSuccessVO(tokenHead, token, null);
    }

    /**
     * 给定登录信息对象，根据用户id查询最新的用户信息/权限信息（其余数据不变），操作【上线/覆盖】到缓存
     * @param loginUser 旧的登录用户信息对象
     */
    public boolean buildLoginUser(LoginUser loginUser) {
        if (loginUser == null) {
            return false;
        }
        long userId = loginUser.getUser().getId();
        LoginUser newLoginUser = SerializationUtils.clone(loginUser);

        // 重新获取用户
        User newUser = iUserService.selectByIdAndBuildCache(userId);
        if (newUser == null) {
            return false;
        }
        newUser.setPassword(null);
        newLoginUser.setUser(newUser);
        newLoginUser.setAuthorities(authorityService.getUserAuthorities(userId));

        // 上线：用户登录信息上线到缓存
        entityCacheService.setEntity(RedisKey.AUTH_LOGIN_USER, loginUser.getUser().getEmail(), loginUser, RedisKey.AUTH_LOGIN_USER_EXPIRE);

        return true;
    }

    /**
     * 从当前线程上下文，获取当前登录用户
     */
    public User getLoginUser(){
        SecurityContext ctx = SecurityContextHolder.getContext();
        Authentication auth = ctx.getAuthentication();
        if(auth == null) {
            // 未登录
            Asserts.fail(ResultCode.UNAUTHORIZED);
        }
        LoginUser loginUser = (LoginUser) auth.getPrincipal();
        return loginUser.getUser();
    }

    /**
     * 从当前线程上下文，获取当前登录用户
     */
    public LoginUser getLoginUserDetails(){
        SecurityContext ctx = SecurityContextHolder.getContext();
        Authentication auth = ctx.getAuthentication();
        if(auth == null) {
            // 未登录
            Asserts.fail(ResultCode.UNAUTHORIZED);
        }
        return  (LoginUser) auth.getPrincipal();
    }

}
